#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include "UnCopy.hpp"
#include "commen.hpp"

const int default_fd = -1;
const uint16_t default_port = 9090;
const std::string default_ip = "127.0.0.1";
const int retry_chance = 5;

class TcpClient
{
public:
    TcpClient(std::string server_ip = default_ip, uint16_t server_port = default_port)
        : _server_ip(server_ip), _server_port(server_port), _socket_fd(default_fd)
    {
        init();
    }

    void init()
    {
        // 创建socket
        _socket_fd = socket(AF_INET, SOCK_STREAM, 0);
        if(_socket_fd < 0)
        {
            log.message(FATAL, "socket error, %d: %s", errno, strerror(errno));
            exit(SOCKET_ERR);
        }
        log.message(INFO, "socket success, socket fd: %d", _socket_fd);

        // 不绑定本端网络信息，tcp发起连接时，内核随机绑定

        // 填写对端网络信息
        bzero(&_server_addr, sizeof(_server_addr));
        _server_addr.sin_family = AF_INET;
        _server_addr.sin_port = htons(_server_port);
        inet_pton(AF_INET, _server_ip.c_str(), &_server_addr.sin_addr); //线程安全的接口 process to net

        // 建立连接
        if(tryConnect() == false)
        {
            exit(CONNECT_ERR);
        }
    }

    bool tryConnect()
    {
        if(getConnection() == false)
        {
            int cnt = retry_chance;
            while(cnt != 0)
            {
                sleep(1);
                log.message(INFO, "server status is unknown, try reconnecting: %d...", cnt);
                if(getConnection() == true)
                {
                    return true;
                }
                cnt--;
            }
            log.message(ERROR, "server offline, write error...");
            return false;
        }
        return true;
    }

    bool getConnection()
    {
        int n = connect(_socket_fd, (struct sockaddr*)&_server_addr, sizeof(_server_addr)); //不产生新socket，仅在建立连接时，对端信息_server_addr使用一次，后续通信不再使用
        if(n < 0)
        {
            log.message(ERROR, "connect error, %d: %s", errno, strerror(errno));
            return false;
        }
        log.message(INFO, "connect success...");
        return true;
    }

    void start()
    {
        while(true)
        {
            std::string write_buffer;
            std::cout << ">> ";
            std::getline(std::cin, write_buffer);
            ssize_t write_n = write(_socket_fd, write_buffer.c_str(), write_buffer.size()); //相比sendto没有输入输出型参数struct sockaddr_in*，因为tcp有链接
            if(write_n == 0)
            {
                break; //输入\n，主动退出
            }
            else if(write_n < 0)
            {
                //未知问题，尝试重连
                if(tryConnect() == false)
                {
                    exit(CONNECT_ERR);
                }
            }
            else
            {
                char read_buffer[1024];
                ssize_t read_n = read(_socket_fd, read_buffer, sizeof(read_buffer));
                if(read_n > 0)
                {
                    read_buffer[read_n] = '\0';
                    std::cout << "echo: " << read_buffer << std::endl;
                }
            }
        }
        std::cout << "client quit..." << std::endl;
    }

    ~TcpClient()
    {
        close(_socket_fd);
    }
private:
    int _socket_fd;
    std::string _server_ip;
    uint16_t _server_port;
    struct sockaddr_in _server_addr;
};